package com.dingwen.treasure.core.aspect;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONUtil;
import com.dingwen.treasure.base.constant.RabbitmqConstant;
import com.dingwen.treasure.base.context.UserVariableContextExt;
import com.dingwen.treasure.base.enums.OperateTypeEnum;
import com.dingwen.treasure.core.annotation.OperationLogRecord;
import com.dingwen.treasure.core.dto.OperationLogDto;
import com.dingwen.treasure.core.util.AspectUtil;
import com.dingwen.treasure.core.util.LogContext;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;


/**
 * 　OperationLogRecordAspect TODO 返回值特定参数，ThreadLocal实现
 * 　@author dingwen
 * 　@date 2022/6/24
 */
@Component
@Aspect
@Slf4j
@RequiredArgsConstructor
public class OperationLogRecordAspect {

    /**
     * rabbitTemplate
     */
    private final RabbitTemplate rabbitTemplate;

    /**
     * 环绕通知
     *
     * @param joinPoint       连接点
     * @param operationRecord 操作记录
     * @return {@link Object}
     */
    @SneakyThrows(Throwable.class)
    @Around("@annotation(operationRecord)")
    public Object doAroundOperationLogRecord(ProceedingJoinPoint joinPoint, OperationLogRecord operationRecord) {

        // 模块
        String module = operationRecord.module();

        // 操作类型
        OperateTypeEnum operateTypeEnum = operationRecord.operateType();

        String elTemplate = operationRecord.id();

        // 请求参数
        boolean request = operationRecord.request();
        String args = null;
        if (request) {
            args = JSONUtil.toJsonStr(joinPoint.getArgs());
        }

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        ApiOperation apiOperation = methodSignature.getMethod().getAnnotation(ApiOperation.class);

        // 资源名称
        String resourceName = apiOperation.value();

        // 操作人
        Long userId = UserVariableContextExt.getUserId();

        // 执行结果
        Object result = joinPoint.proceed();

        // 业务id
        Long businessId = AspectUtil.getValueByEL(joinPoint, Long.class, elTemplate);

        // 模拟当日志需要方法执行过程中当结果作为参数时，采用TheadLocal实现
        String methodKey = operationRecord.methodKey();
        String value = LogContext.get(methodKey);
        log.info("方法直接过程参数获取：{}", value);
        LogContext.remove(methodKey);

        // 时间
        // 构建日志
        OperationLogDto operationLogDto = OperationLogDto.builder()
                .detail(JSONUtil.toJsonStr(result))
                .operateTime(LocalDateTimeUtil.formatNormal(LocalDateTime.now()))
                .businessId(String.valueOf(businessId))
                .module(module)
                .request(args)
                .resourceName(resourceName)
                .operatorId(String.valueOf(userId))
                .operateType(operateTypeEnum.getDescription())
                .build();
        // mq  send log 存 mongo
        rabbitTemplate.convertAndSend(RabbitmqConstant.DIRECT_EXCHANGE_OPERATOR_LOG,
                RabbitmqConstant.ROUTER_KEY_OPERATOR_LOG,
                JSONUtil.toJsonStr(operationLogDto),
                new CorrelationData(String.valueOf(userId)));
        return result;
    }
}
